package com.example.friend.manger;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.example.core.constants.CacheConstants;
import com.example.core.domain.TableDataInfo;
import com.example.friend.domain.ExamQuestion;
import com.example.friend.domain.Question;
import com.example.friend.domain.vo.QuestionESVO;
import com.example.friend.domain.vo.QuestionVO;
import com.example.friend.elasticsearch.QuestionRepository;
import com.example.friend.mapper.ExamQuestionMapper;
import com.example.friend.mapper.QuestionMapper;
import com.example.friend.mapper.UserSubmitMapper;
import com.example.redis.service.RedisService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class QuestionCacheManager {

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Autowired
    private RedisService redisService;

    public TableDataInfo selectQuestionList(QuestionRepository questionRepository, int start, int end) {
        String timeListKey = getTimeListKey();
        return getQuestionList(questionRepository, null, timeListKey, null, start, end);
    }

    public TableDataInfo selectQuestionListByDifficult(QuestionRepository questionRepository, Integer difficulty, int start, int end) {
        String diffListKey = getDiffListKey(difficulty);
        return getQuestionList(questionRepository, difficulty, diffListKey, null, start, end);
    }

    public QuestionESVO detail(QuestionRepository questionRepository, Long questionId) {
        return questionRepository.findById(questionId).orElse(null);
    }

    public TableDataInfo selectExamQuestionList(QuestionRepository questionRepository, Long examId, int start, int end) {
        String key = getExamQuestionListKey(examId);
        return getQuestionList(questionRepository, null, key, examId, start, end);
    }

    public List<QuestionVO> selectHotQuestionList(QuestionRepository questionRepository) {
        String key = getHotQuestionKey();
        List<Long> hotQuestionIdList = redisService.getCacheListByRange(key, 0, -1, Long.class);
        if (CollectionUtils.isEmpty(hotQuestionIdList)) {
            hotQuestionIdList = userSubmitMapper.selectHotQuestionList();
            if (CollectionUtils.isEmpty(hotQuestionIdList)) {
                return new ArrayList<>();
            }
            redisService.rightPushAll(key, hotQuestionIdList);
        }
        return assembleQuestionVOList(questionRepository, hotQuestionIdList);
    }

    /**
     * 上一题
     *
     * @param questionId 当前题目id
     * @return 目标题目id
     */
    public Long preQuestion(Long questionId) {
        List<Long> questionIdList = redisService.getCacheListByRange(getTimeListKey(), 0, -1, Long.class);
        int index = questionIdList.indexOf(questionId);
        if (index > 0) {
            // 返回前一个元素
            return questionIdList.get(index - 1);
        } else {
            // 当前元素是列表的第一个元素，没有前一个元素
            return null;
        }
    }

    /**
     * 上一题
     *
     * @param questionId 当前题目id
     * @return 目标题目id
     */
    public Long nextQuestion(Long questionId) {
        List<Long> questionIdList = redisService.getCacheListByRange(getTimeListKey(), 0, -1, Long.class);
        int index = questionIdList.indexOf(questionId);
        if (index >= 0 && index < questionIdList.size() - 1) {
            // 返回后一个元素
            return questionIdList.get(index + 1);
        } else {
            // 当前元素是列表的最后一个元素，没有后一个元素
            return null;
        }
    }

    private TableDataInfo getQuestionList(QuestionRepository questionRepository, Integer difficult,
                                          String key, Long examId, int start, int end) {
        List<Long> questionIdList = redisService.getCacheListByRange(key, start, end - 1, Long.class);
        List<QuestionVO> questionVOList;
        TableDataInfo info = TableDataInfo.empty();
        if (CollectionUtils.isEmpty(questionIdList)) {
            refreshQuestionList(questionRepository, examId, difficult, key);
            questionIdList = redisService.getCacheListByRange(key, start, end - 1, Long.class);
        }
        if (CollectionUtils.isEmpty(questionIdList)) {
            return info;
        }
        long total = redisService.getListSize(key);
        questionVOList = assembleQuestionVOList(questionRepository, questionIdList);

        info.setTotal(total);
        info.setRows(questionVOList);
        return info;
    }

    private void refreshQuestionList(QuestionRepository questionRepository, Long examId, Integer difficult, String key) {
        List<Long> questionIdList = new ArrayList<>();

        if (examId != null) {
            List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                    .select(ExamQuestion::getQuestionId)
                    .eq(ExamQuestion::getExamId, examId));

            if (!CollectionUtils.isEmpty(examQuestionList)) {
                questionIdList.addAll(examQuestionList.stream().map(ExamQuestion::getQuestionId).toList());
                refreshDetail(questionRepository, null, questionIdList);
            }
        } else {
            List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                    .eq(difficult != null, Question::getDifficulty, difficult)
                    .orderByDesc(Question::getCreateTime));

            if (!CollectionUtils.isEmpty(questionList)) {
                questionIdList.addAll(questionList.stream().map(Question::getQuestionId).toList());
                refreshDetail(questionRepository, questionList, questionIdList);
            }
        }

        if (!questionIdList.isEmpty()) {
            redisService.rightPushAll(key, questionIdList);
        }
    }

    private List<QuestionVO> assembleQuestionVOList(QuestionRepository questionRepository, List<Long> questionIdList) {
        Map<Long, QuestionESVO> questionMap = getQuestionMap(questionRepository, questionIdList);
        List<QuestionVO> resultList = new ArrayList<>();
        for (Long questionId : questionIdList) {
            QuestionVO questionVO = new QuestionVO();
            questionVO.setQuestionId(questionId);
            resultList.add(questionVO);
            if (CollectionUtils.isEmpty(questionMap)) {
                continue;
            }
            QuestionESVO question = questionMap.get(questionId);
            if (question == null) {
                Question questionByDB = questionMapper.selectById(questionId);
                if (questionByDB == null) {
                    continue;
                }
                question = new QuestionESVO();
                BeanUtils.copyProperties(questionByDB, question);
                questionRepository.save(question);
            }
            questionVO.setTitle(question.getTitle());
            questionVO.setDifficulty(question.getDifficulty());
        }
        return resultList;
    }

    private Map<Long, QuestionESVO> getQuestionMap(QuestionRepository questionRepository, List<Long> questionIdList) {
        if (CollectionUtils.isEmpty(questionIdList)) {
            return null;
        }
        List<String> questionIdStrList = new ArrayList<>();
        for (Long questionId : questionIdList) {
            questionIdStrList.add(String.valueOf(questionId));
        }
        List<QuestionESVO> questionESVOList = questionRepository.findQuestionESVOByQuestionIdIn(questionIdList);
        if (CollectionUtils.isEmpty(questionESVOList)) {
            List<Question> questionList = questionMapper.selectBatchIds(questionIdList);
            if (CollectionUtils.isEmpty(questionList)) {
                return null;
            }
            questionESVOList = new ArrayList<>();
            for (Question question : questionList) {
                QuestionESVO questionESVO = new QuestionESVO();
                BeanUtils.copyProperties(question, questionESVO);
                questionESVOList.add(questionESVO);
            }
            questionRepository.saveAll(questionESVOList);
        }

        return questionESVOList.stream().collect(
                Collectors.toMap(QuestionESVO::getQuestionId, Function.identity()));
    }

    private Map<Long, Question> refreshDetail(QuestionRepository questionRepository, List<Question> questionList,
                                              List<Long> questionIdList) {
        if (CollectionUtils.isEmpty(questionList)) {
            questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>().
                    in(Question::getQuestionId, questionIdList));
        }
        if (CollectionUtils.isEmpty(questionList)) {
            return null;
        }
        /* 使用redis 作为缓存存储题目详情 */
//        redisService.setCacheMap(getDetailKey(), questionDetailMap);
        Map<Long, Question> questionDetailMap = new HashMap<>();
        List<QuestionESVO> questionESVOList = new ArrayList<>();
        for (Question question : questionList) {
            questionDetailMap.put(question.getQuestionId(), question);
            QuestionESVO questionESVO = new QuestionESVO();
            BeanUtils.copyProperties(question, questionESVO);
            questionESVO.setQuestionId(question.getQuestionId());
            questionESVOList.add(questionESVO);
        }
        questionRepository.saveAll(questionESVOList);
        return questionDetailMap;
    }

    private String getHotQuestionKey() {
        return CacheConstants.QUESTION_HOT_LIST;
    }

    private String getExamQuestionListKey(Long examId) {
        return CacheConstants.EXAM_QUESTION_LIST + examId;
    }

    private String getTimeListKey() {
        return CacheConstants.QUESTION_TIME_LIST;
    }

    private String getDiffListKey(Integer difficulty) {
        return CacheConstants.QUESTION_DIFF_LIST + difficulty;
    }

    private String getDetailKey() {
        return CacheConstants.QUESTION_DETAIL;
    }
}
